﻿#region Using References

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using Beaker.OpenCube.Game;
using Beaker.OpenCube.Specs.Builders;
using Beaker.OpenCube.Textures;
using Beaker.Services.FileSystem;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

#endregion

namespace Beaker.OpenCube.Specs.Textures
{
	[ExcludeFromCodeCoverage]
	[TestClass]
	public class TexturePackReaderSpecs
	{
		private Stream OpenDefaultTexturePack()
		{
			// using the mincraft jar as input for default texture pack
			// the jar is a zip, and structure equals that of a texture pack with extra's
			// we just ignore the classes and extra folders.
			string appDataPath = Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData );
			string gameJarPath = Path.Combine( appDataPath, ".minecraft", "versions", "1.5.2", "1.5.2.jar" );
			return File.OpenRead( gameJarPath );
		}

		[TestMethod]
		public void MyTestMethod()
		{
			using( Stream stream = OpenDefaultTexturePack() )
			{
				TexturePackReader reader = TexturePackReader.FromStream( stream );
				IReadOnlyList<BlockTexture> textures = reader.ReadAllAvailableBlockTextures();
			}
		}

		#region FromStream

		[TestMethod]
		public void FromStreamThrowsWhenStreamIsNull()
		{
			// act
			Action act = () => TexturePackReader.FromStream( null );

			// assert
			act.ShouldThrow<ArgumentNullException>();
		}

		[TestMethod]
		public void FromStreamThrowsWhenStreamIsNotSeekable()
		{
			// arrange
			var stream = new ConfigurableStream( new MemoryStream() );
			stream.DisableSeeking();

			// act
			Action act = () => TexturePackReader.FromStream( stream );

			// assert
			act.ShouldThrow<ArgumentException>();
		}

		[TestMethod]
		public void FromStreamThrowsWhenStreamIsNotReadable()
		{
			// arrange
			var stream = new ConfigurableStream( new MemoryStream() );
			stream.DisableReading();

			// act
			Action act = () => TexturePackReader.FromStream( stream );

			// assert
			act.ShouldThrow<ArgumentException>();
		}

		[TestMethod]
		public void FromStreamThrowsFormatExceptionWhenStreamIsNotAZip()
		{
			// arrange
			var stream = new MemoryStream();

			// act
			Action act = () => TexturePackReader.FromStream( stream );

			// assert
			act.ShouldThrow<FormatException>();
		}

		[TestMethod]
		public void FromStreamThrowsFormatExceptionWhenFallbackStreamIsNotAZip()
		{
			// arrange
			Stream stream = new ZipStreamBuilder().Build();
			var fallbackStream = new MemoryStream();

			// act
			Action act = () => TexturePackReader.FromStream( stream, fallbackStream );

			// assert
			act.ShouldThrow<FormatException>();
		}

		[TestMethod]
		public void ZipInputStreamResultsInTexturePackReader()
		{
			// arrange
			Stream stream = new ZipStreamBuilder().Build();

			// act
			TexturePackReader reader = TexturePackReader.FromStream( stream );

			// assert
			reader.Should().NotBeNull();
		}
		#endregion
		#region ReadBlockTexture

		[TestMethod]
		public void ReadingSimpleExistingBlockReturnsBlockTexture()
		{
			// arrange
			Stream textureStream =
				GetType().Assembly.GetManifestResourceStream( GetType(), "DummyTexture.png" );
			IFileSystemService files = new FileSystemServiceBuilder()
				.WithFile( "textures/blocks/dirt.png", textureStream )
				.Build();
			Stream stream = new ZipStreamBuilder()
				.WithFilesFrom( files )
				.Build();
			TexturePackReader reader = TexturePackReader.FromStream( stream );

			// act
			BlockTexture blockTexture = reader.ReadBlockTexture( Block.Dirt );

			// assert
			blockTexture.Should().NotBeNull();
		}

		[TestMethod]
		public void ReadingSimpleMissingBlockReturnsNull()
		{
			// arrange
			Stream stream = new ZipStreamBuilder()
				.Build();
			TexturePackReader reader = TexturePackReader.FromStream( stream );

			// act
			BlockTexture blockTexture = reader.ReadBlockTexture( Block.Dirt );

			// assert
			blockTexture.Should().BeNull();
		}

		[TestMethod]
		public void ReadingSimpleMissingBlockReturnsTextureFromFallback()
		{
			// arrange
			Stream stream = new ZipStreamBuilder()
				.Build();
			Stream textureStream =
				GetType().Assembly.GetManifestResourceStream( GetType(), "DummyTexture.png" );
			IFileSystemService files = new FileSystemServiceBuilder()
				.WithFile( "textures/blocks/dirt.png", textureStream )
				.Build();
			Stream fallbackStream = new ZipStreamBuilder()
				.WithFilesFrom( files )
				.Build();
			TexturePackReader reader = TexturePackReader.FromStream( stream, fallbackStream );

			// act
			BlockTexture blockTexture = reader.ReadBlockTexture( Block.Dirt );

			// assert
			blockTexture.Should().NotBeNull();
		}

		[TestMethod]
		public void ReadingBlockWithTextReturnsTextureWithFrames()
		{
			// arrange
			Stream textureStream =
				GetType().Assembly.GetManifestResourceStream( GetType(), "DummyTexture.png" );
			IFileSystemService files = new FileSystemServiceBuilder()
				.WithFile( "textures/blocks/dirt.png", textureStream )
				.WithFile( "textures/blocks/dirt.txt", new TextStreamBuilder().WithText( "0*4,1*4" ).Build() )
				.Build();
			Stream stream = new ZipStreamBuilder()
				.WithFilesFrom( files )
				.Build();
			TexturePackReader reader = TexturePackReader.FromStream( stream );

			// act
			BlockTexture blockTexture = reader.ReadBlockTexture( Block.Dirt );

			// assert
			blockTexture.Textures[BlockTextureFlags.Top].FrameCount.Should().Be( 8 );
		}


		[TestMethod]
		public void WhenFallbackTextureIsUsedTextFileInOriginalIsIgnored()
		{
			// arrange
			IFileSystemService files = new FileSystemServiceBuilder()
				.WithFile( "textures/blocks/dirt.txt", "1*4,0*4,2*4" )
				.Build();
			Stream stream = new ZipStreamBuilder()
				.WithFilesFrom( files )
				.Build();

			Stream textureStream =
				GetType().Assembly.GetManifestResourceStream( GetType(), "DummyTexture.png" );
			IFileSystemService fallBackFiles = new FileSystemServiceBuilder()
				.WithFile( "textures/blocks/dirt.png", textureStream )
				.Build();
			Stream fallbackStream = new ZipStreamBuilder()
				.WithFilesFrom( fallBackFiles )
				.Build();

			TexturePackReader reader = TexturePackReader.FromStream( stream, fallbackStream );

			// act
			BlockTexture blockTexture = reader.ReadBlockTexture( Block.Dirt );

			// assert
			blockTexture.Textures[BlockTextureFlags.Top].FrameCount.Should().Be( 1 );
		}

		[TestMethod]
		public void WhenInvlidFrameInfoEncounteredShouldThrowFormatException()
		{
			// arrange
			Stream textureStream =
				GetType().Assembly.GetManifestResourceStream( GetType(), "DummyTexture.png" );
			IFileSystemService files = new FileSystemServiceBuilder()
				.WithFile( "textures/blocks/dirt.png", textureStream )
				.WithFile( "textures/blocks/dirt.txt", new TextStreamBuilder().WithText( "foobar" ).Build() )
				.Build();
			Stream stream = new ZipStreamBuilder()
				.WithFilesFrom( files )
				.Build();
			TexturePackReader reader = TexturePackReader.FromStream( stream );

			// act
			Action act = () => reader.ReadBlockTexture( Block.Dirt );

			// assert
			act.ShouldThrow<FormatException>();
		}

		[TestMethod]
		public void WhenAnUnsupportedBlockIsRequestedShouldReturnNull()
		{
			// arrange
			var stream = new ZipStreamBuilder();
			TexturePackReader reader = TexturePackReader.FromStream( stream.Build() );

			// act (we try to read air texture, it should not be mapped, because it has no texture)
			BlockTexture blockTexture = reader.ReadBlockTexture( Block.Air );

			// assert
			blockTexture.Should().BeNull();
		}

		#endregion
	}
}
